package cn.com.sparkle.paxos.stablestorage.model;

import java.io.IOException;
import java.util.concurrent.Callable;

import cn.com.sparkle.paxos.stablestorage.io.BufferedFileOut;
import cn.com.sparkle.paxos.util.Adler32Calc;

public final class RecordHead {
	private byte[] head;
	private Integer bodySize = null;
	private Long instanceId = null;
	private RecordType type = null;

	public RecordHead(int bodySize, long instanceId, RecordType type) {
		head = new byte[20];
		head[0] = (byte) ((bodySize >>> 24) & 0xFF | type.getValue());
		head[1] = (byte) ((bodySize >>> 16) & 0xFF);
		head[2] = (byte) ((bodySize >>> 8) & 0xFF);
		head[3] = (byte) ((bodySize >>> 0) & 0xFF);
		head[4] = (byte) (0xff & (instanceId >> 56));
		head[5] = (byte) (0xff & (instanceId >> 48));
		head[6] = (byte) (0xff & (instanceId >> 40));
		head[7] = (byte) (0xff & (instanceId >> 32));
		head[8] = (byte) (0xff & (instanceId >> 24));
		head[9] = (byte) (0xff & (instanceId >> 16));
		head[10] = (byte) (0xff & (instanceId >> 8));
		head[11] = (byte) (0xff & instanceId);

		long checksum = Adler32Calc.calc(head, 0, 12);

		head[12] = (byte) (0xff & (checksum >> 56));
		head[13] = (byte) (0xff & (checksum >> 48));
		head[14] = (byte) (0xff & (checksum >> 40));
		head[15] = (byte) (0xff & (checksum >> 32));
		head[16] = (byte) (0xff & (checksum >> 24));
		head[17] = (byte) (0xff & (checksum >> 16));
		head[18] = (byte) (0xff & (checksum >> 8));
		head[19] = (byte) (0xff & checksum);
		this.bodySize = bodySize;
		this.type = type;
		this.instanceId = instanceId;
	}

	public RecordHead(byte[] head) {
		this.head = head;
	}

	public boolean checkSum() {
		long checksum = Adler32Calc.calc(head, 0, 12);
		if (checksum == (((long) head[12] << 56)
				+ ((long) (head[13] & 255) << 48)
				+ ((long) (head[14] & 255) << 40)
				+ ((long) (head[15] & 255) << 32)
				+ ((long) (head[16] & 255) << 24) + ((head[17] & 255) << 16)
				+ ((head[18] & 255) << 8) + ((head[19] & 255) << 0))) {
			return true;
		} else {
			return false;
		}
	}

	public int getBodySize() {
		if (bodySize == null) {
			bodySize = ((head[0] & (~0x80)) << 24) + ((head[1] & 255) << 16)
					+ ((head[2] & 255) << 8) + ((head[3] & 255) << 0);
		}
		return bodySize;
	}

	public long getInstanceId() {
		if (instanceId == null) {
			instanceId = ((long) head[4] << 56)
					+ ((long) (head[5] & 255) << 48)
					+ ((long) (head[6] & 255) << 40)
					+ ((long) (head[7] & 255) << 32)
					+ ((long) (head[8] & 255) << 24) + ((head[9] & 255) << 16)
					+ ((head[10] & 255) << 8) + ((head[11] & 255) << 0);
		}
		return instanceId;
	}

	public RecordType getType() {
		if (type == null) {
			if ((head[0] & 0x80) == RecordType.SUCCESS.getValue()) {
				type = RecordType.SUCCESS;
			} else {
				type = RecordType.VOTE;
			}
		}
		return type;
	}

	public byte[] getBytes() {
		return head;
	}

	public void writeToStream(BufferedFileOut out, Callable<Object> callable)
			throws IOException {
		out.write(this.head, 0, 20, callable);
	}
}
